#include <lore/assembler.hpp>

#include <iostream>
#include <fstream>
#include <string>
#include <boost/bind.hpp>



namespace {

std::list<std::string> read_words() {
	std::list<std::string> ret;

	std::string v;
	std::cin >> v;

	unsigned pos = 0;

	do {
		while (pos < v.size() && ::isspace(v[pos])) {
			++pos; // eat starting space
		}
		unsigned new_pos = std::min(v.find(' ', pos), v.size()-1);

		unsigned diff = new_pos - pos;

		if (diff > 0)
			ret.push_back(v.substr(pos, diff+1));

		pos = new_pos;
	} while (pos != v.size() - 1);

	return ret;
}

template <typename T>
std::list<T> & operator+=(std::list<T> & first, std::list<T> const& second)
{
	std::copy(second.begin(), second.end(), std::back_inserter(first));

	return first;
}


void run_save(Program & program, std::list<std::string> & args)
{
	while (true) {
		while (args.empty()) {
			std::cout << "Enter the filename: " << std::endl;
			args += read_words();
		}

		std::string filename(args.front());
		args.pop_front();

		if (true) { // no error
			std::ofstream out;
			out.open(filename.c_str(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
			program.write(out);
			out.flush();

			std::cout << "Great Success! Written to '" << filename << '"' << std::endl;
			return;
		}
	}
}


void run_open(Program & program, std::list<std::string> & args)
{
	while (true) {
		while (args.empty()) {
			std::cout << "Would you like to 'execute', 'disassemble', 'save' or 'exit'? ";
			args += read_words();
		}

		std::string arg(args.front());
		args.pop_front();

		if (arg == "execute") {
			Program::ExecutionResult res(program.execute());

			std::cout << "Return code: " << res.return_code() << ", Cost: " << res.cost() << ", Error Code: " << res.error() << std::endl;

		} else if (arg == "disassemble") {
			std::cout << program.disassemble() << std::endl;
		} else if (arg == "save") {
			run_save(program, args);
		} else if (arg == "exit") {
			return;
		} else {
			std::cout << "Unknown command '" << arg << '\'' << std::endl;
		}


	}
}


void run_assemble(std::list<std::string> & args)
{
	while (args.empty()) {
		std::cout << "File name of lasm file: ";
		args += read_words();
	}

	std::string arg(args.front());
	args.pop_front();

	// TODO error handling...

	std::ifstream in;
	in.open(arg.c_str());
	Program p(assemble(in));

	run_open(p, args);
}

void run_load(std::list<std::string> & args)
{
	while (args.empty()) {
		std::cout << "File name of the lobj: " << std::endl;
		args += read_words();
	}

	std::string prog_name(args.front());
	args.pop_front();

	std::ifstream in;
	in.open(prog_name.c_str());

	Program p(in);

	run_open(p, args);
}


void run(std::list<std::string> & args)
{

	while (true) {
		while (args.empty()) {
			std::cout << "'assemble', 'open' or 'exit'? ";
			std::list<std::string> just_read(read_words());
			args += just_read;
		}

		std::string arg(args.front());
		args.pop_front();

		if (arg == "assemble") {
			run_assemble(args);
		} else if (arg == "load") {
			run_load(args);
		} else if (arg == "exit") {
			return;
		} else {
			std::cout << "Error: '" << arg << "'' is not valid" << std::endl;
		}
	}
}

}


int main(int argc, char const * const * argv)
{

	std::list<std::string> input;

	for (int i(1) ; i < argc ; ++i) {
		input.push_back(argv[i]);
	}

	run(input);

	return 0;
}

